import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;

// # CSV编码规则
// - ','间隔列;"\r\n"间隔行(最后一行可能没有换行符结尾)
// - 文本含有','或'\r',或以'"'开头,则整个内容两边加'"'
// - 两边加'"'的情况下,里面的每个'"'改成两个
// # 简单&高性能的实现,需要严格按以上规则编码,需要JDK9以上
public class FastCsv {
	private static final VarHandle longLeVH = MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN);

	public interface Handler {
		void onCell(int pos, int len); // len<0: next line
	}

	public static void parseCsv(byte[] csv, Handler handler) { // csv maybe modified
		parseCsv(csv, 0, csv.length, handler);
	}

	public static void parseCsv(byte[] csv, int pos, int len, Handler handler) { // csv maybe modified
		for (int end = pos + len; pos < end; ) {
			int b = csv[pos];
			if (b == ',')
				handler.onCell(pos++, 0);
			else if (b == '\r') {
				handler.onCell(pos, 0);
				handler.onCell(pos, -1);
				pos += 2;
			} else if (b == '"') {
				int e = scanQuot(csv, ++pos, end), p = e, q;
				while ((q = e + 1) < end && (b = csv[q]) == '"') {
					int n = (e = scanQuot(csv, q + 1, end)) - q;
					System.arraycopy(csv, q, csv, p, n);
					p += n;
				}
				handler.onCell(pos, p - pos);
				if (b == '\r') {
					handler.onCell(e + 1, -1);
					pos = e + 3;
				} else
					pos = e + 2;
			} else {
				int e = scanCellEnd(csv, pos + 1, end);
				handler.onCell(pos, e - pos);
				pos = e + 1;
			}
		}
	}

	private static int scanCellEnd(byte[] csv, int pos, int end) {
		for (; pos + 8 <= end; pos += 8) {
			long b8 = (long)longLeVH.get(csv, pos);
			long p0 = b8 ^ 0x2c2c2c2c_2c2c2c2cL; // ','
			long p1 = b8 ^ 0x0d0d0d0d_0d0d0d0dL; // '\r'
			p0 = (p0 - 0x01010101_01010101L) & ~p0 & 0x80808080_80808080L;
			p1 = (p1 - 0x01010101_01010101L) & ~p1 & 0x80808080_80808080L;
			if ((p0 |= p1) != 0)
				return pos + (Long.numberOfTrailingZeros(p0) >> 3);
		}
		for (; pos < end; pos++) {
			int b = csv[pos];
			if (b == ',' || b == '\r')
				return pos;
		}
		return end;
	}

	private static int scanQuot(byte[] csv, int pos, int end) {
		for (; pos + 8 <= end; pos += 8) {
			long p = (long)longLeVH.get(csv, pos) ^ 0x22222222_22222222L; // '"'
			p = (p - 0x01010101_01010101L) & ~p & 0x80808080_80808080L;
			if (p != 0)
				return pos + (Long.numberOfTrailingZeros(p) >> 3);
		}
		for (; pos < end; pos++) {
			if (csv[pos] == '"')
				return pos;
		}
		return end;
	}

	public static void main(String[] args) {
		/*	----------------------------
			|ab|a,b|"ab"|a"b| ab |"a,b"|
			----------------------------
			|a|
			|b|
			----------------------------
			||||||ab|
			----------------------------
			|ab"|||||a"b|
			----------------------------*/
		byte[] csv = """
				ab,"a,b",""\"ab""\",a"b, ab ,""\"a,b""\"\r
				"a
				b",,,,,\r
				,,,,,"ab"\r
				ab",,,,,"a""b\"""".getBytes(StandardCharsets.UTF_8);
		System.out.println("---");
		System.out.println(new String(csv, StandardCharsets.UTF_8));
		System.out.println("---");
		parseCsv(csv, (pos, len) -> {
			if (len < 0)
				System.out.println("---");
			else
				System.out.println("[" + new String(csv, pos, len, StandardCharsets.UTF_8) + "]");
		});
		System.out.println("===");
	}
}
